package com.bclz.errorhandling


sealed trait Either[+E,+A] {


  def map[B](f:A=>B):Either[E,B]=this match {
    case Left(v)=>Left(v)
    case Right(v)=>
      try Right(f(v))
      catch {
        case e:E=>Left(e)
      }

  }
  def flatMap[EE>:E,B](f:A=>Either[EE,B]):Either[EE,B] = this match {

    case Left(v)=>Left(v)
    case Right(v)=>f(v)
  }



  def orElse[EE>:E,B>:A](b: =>Either[EE,B]):Either[EE,B] = this match {
    case Left(_)=>b
    case Right(_)=>this
  }
  def map2[EE>:E,B,C](b:Either[EE,B])(f:(A,B)=>C):Either[EE,C] =
    for{
      aa <- this
      bb <- b
    } yield f(aa,bb)


}

object Either{


  def Try[A](a: =>A): Either[Exception,A] ={

    try Right(a)
    catch{
      case e:Exception =>Left(e)
    }

  }


  def sequence[E,A](l:List[Either[E,A]]):Either[E,List[A]]= l match {
    case Nil =>Right(Nil)
    case h::t=>
      h flatMap(a=>sequence(t).map(s=>a::s))
  }

  def traverse[E,A,B](l:List[A])(f:A=>B):Either[E,List[B]]={
      l.foldRight[Either[E,List[B]]](Right(Nil))((a,b1)=>b1.map(b=>f(a)::b))
  }

}

case class Left[+E](e:E) extends Either[E,Nothing]
case class Right[+A](v:A) extends Either[Nothing,A]
